using System;
using System.Collections.Generic;
using System.Text;

using Mud.Common;

namespace Mud.Server
{

    /// <summary>
    /// Method scheduler.
    /// </summary>
    public class Scheduler
    {

        private List<ScheduleItem> _listOfMethods = new List<ScheduleItem>();

        /// <summary>
        /// Schedules the method.
        /// </summary>
        /// <param name="timeIndex">Index of the time.</param>
        /// <param name="method">The method.</param>
        public void ScheduleMethod(int timeIndex, ScheduledMethod method)
        {
            _listOfMethods.Add(new ScheduleItem(timeIndex, method));
            _listOfMethods.Sort();
            Logger.Log(LogType.Debug, "Scheduler.ScheduleMethod", "Scheduled on {0} {1}.{3} to run at {2}.", TimeFormater.FormatTimeIndex(Environment.TickCount), method.Target, method.Method.Name, TimeFormater.FormatTimeIndex(timeIndex));
        }

        /// <summary>
        /// Removes the scheduled method.
        /// </summary>
        /// <param name="method">The method.</param>
        public void RemoveScheduledMethod(ScheduledMethod method)
        {
            foreach (ScheduleItem item in _listOfMethods)
            {
                if (item.Method == method)
                {
                    _listOfMethods.Remove(item);
                    return;
                }
            }
        }

        /// <summary>
        /// Determines whether the method is scheduled.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <returns>
        /// 	<c>true</c> if the method is scheduled; otherwise, <c>false</c>.
        /// </returns>
        public bool IsMethodScheduled(ScheduledMethod method)
        {
            foreach (ScheduleItem item in _listOfMethods)
            {
                if (item.Method == method)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Executes scheduled methods.
        /// </summary>
        /// <param name="currentTimeIndex">Current time index.</param>
        public void ExecuteScheduledMethods(int currentTimeIndex)
        {
            if (_listOfMethods.Count == 0)
                return;


            for (int i = 0; i < _listOfMethods.Count; i++)
            {
                if (_listOfMethods[0].TimeIndex > currentTimeIndex) // nothing to check at the moment
                    return;

                ScheduleItem item = _listOfMethods[0];
                try
                {
                    Logger.Log(LogType.Debug, "Scheduler.ExecuteScheduledMethods", "Executing {0}.{1} at {2} after waiting for {3}.", item.Method.Target, item.Method.Method.Name, TimeFormater.FormatTimeIndex(currentTimeIndex), TimeFormater.FormatTimeIndex(currentTimeIndex, item.TimePlacedOnSchedule));
                    item.Method();
                }
                catch (Exception ex)
                {
                    Logger.Log(LogType.Error, "Scheduler.ExecuteScheduledMethods", "There was an error while executing method {0}.{1}. Error {2}", item.Method.Target, item.Method.Method.Name, ex);
                }
                RemoveScheduledMethod(item.Method);
                i = 0;
            }

        }


        public delegate void ScheduledMethod();

        private class ScheduleItem : IComparable<ScheduleItem>
        {
            public int TimeIndex
            {
                get { return _timeIndex; }
            }
            private int _timeIndex;

            public ScheduledMethod Method
            {
                get { return _method; }
            }
            private ScheduledMethod _method;


            public int TimePlacedOnSchedule
            {
                get { return _placed; }
            }
            private int _placed;


            public ScheduleItem(int index, ScheduledMethod method)
            {
                if (method == null)
                    throw new ArgumentNullException("method");
                _timeIndex = index;
                _method = method;
                _placed = Environment.TickCount;
            }

            #region IComparable<ScheduleItem> Members

            public int CompareTo(ScheduleItem other)
            {
                return this._timeIndex.CompareTo(other._timeIndex);
            }

            #endregion

            public override bool Equals(object obj)
            {
                ScheduleItem item = obj as ScheduleItem;
                if (item == null)
                    throw new InvalidOperationException();
                return Equals(item);
            }

            public bool Equals(ScheduleItem item)
            {
                if (item._method == this._method)
                    return true;

                return false;
            }

            public override int GetHashCode()
            {
                return _method.GetHashCode();
            } 

        }

    }


}
